/*
 *	$Header: $
 *
 *	$History: $
 */
#include <stdafx.h>
#include <stdlib.h>
#include <io.h>
#include <sys\stat.h>
#include <shlobj.h>
#include <lm.h>
#include <atlconv.h>
#include <shlwapi.h>

#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "mpr.lib")

#ifdef countof
#undef countof
#endif

#define countof(x) (sizeof(x) / sizeof((x)[0]))

CFileSpec::CFileSpec(FS_BUILTINS eSpec)
{
	Initialise(eSpec);
}

CFileSpec::CFileSpec(FS_BUILTINS eSpec, LPCTSTR szFilename)
{
	ASSERT(szFilename);
	ASSERT(AfxIsValidString(szFilename));

	Initialise(eSpec);
	SetFileName(szFilename);
}

CFileSpec::CFileSpec(LPCTSTR szSpec, LPCTSTR szFilename)
{
	ASSERT(szFilename);
	ASSERT(AfxIsValidString(szFilename));

	SetFullSpec(szSpec);
	SetFileName(szFilename);
}

CFileSpec::CFileSpec(LPCTSTR szFilename)
{
	ASSERT(szFilename);
	ASSERT(AfxIsValidString(szFilename));

	SetFullSpec(szFilename);
}

void CFileSpec::Initialise(FS_BUILTINS eSpec)
{
	TCHAR tszPath[_MAX_PATH],
		  *ptsz;

	switch (eSpec)
	{
	case FS_EMPTY:								//	Nothing
		break;

	case FS_APP:								//	Full application path and name
		GetModuleFileName(NULL, tszPath, countof(tszPath));
		SetFullSpec(LPCTSTR(tszPath));
		break;

	case FS_APPDIR:								//	Application folder
		GetModuleFileName(NULL, tszPath, countof(tszPath));
		SetFullSpec(LPCTSTR(tszPath));
		m_csExtension = _T("");
		m_csFilename = _T("");
		break;

	case FS_WINDIR:								//	Windows folder
		GetWindowsDirectory(tszPath, countof(tszPath));
		PathAddBackslash(tszPath);
		SetFullSpec(LPCTSTR(tszPath));
		break;

	case FS_SYSDIR:								//	System folder
		GetSystemDirectory(tszPath, countof(tszPath));
		PathAddBackslash(tszPath);
		SetFullSpec(LPCTSTR(tszPath));
		break;

	case FS_TMPDIR:								//	Temporary folder
		GetTempPath(countof(tszPath), tszPath);
		SetFullSpec(LPCTSTR(tszPath));
		break;

	case FS_DESKTOP:							//	Desktop folder
		GetShellFolder(CSIDL_DESKTOP);
		break;

	case FS_FAVOURITES:							//	Favourites folder
		GetShellFolder(CSIDL_FAVORITES);
		break;

	case FS_TEMPNAME:
		GetTempPath(countof(tszPath), tszPath);
#ifdef _UNICODE
		_tcscpy(tszPath, ptsz = _wtempnam(tszPath, _T("~")));
#else
		_tcscpy(tszPath, ptsz = _tempnam(tszPath, _T("~")));
#endif
		SetFullSpec(LPCTSTR(tszPath));
		free(ptsz);
		break;

	case FS_MEDIA:
		GetWindowsDirectory(tszPath, countof(tszPath));
		PathAddBackslash(tszPath);
		_tcscat(tszPath, _T("media\\"));
		SetFullSpec(LPCTSTR(tszPath));
		break;

	case FS_CURRDIR:
		GetCurrentDirectory(countof(tszPath), tszPath);
		PathAddBackslash(tszPath);
		SetFullSpec(LPCTSTR(tszPath));
		break;

	default:
#ifdef _DEBUG
		TRACE(_T("Invalid initialisation spec for CFileSpec, %d\n"), eSpec);
#endif
		ASSERT(NULL);
	}
}

void CFileSpec::GetShellFolder(int iFolder)
{
	LPITEMIDLIST pItemList;
	LPMALLOC	 pMalloc;

	SHGetMalloc(&pMalloc);

	if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, iFolder, &pItemList)))
	{
		TCHAR tszPath[_MAX_PATH];

		if (SHGetPathFromIDList(pItemList, tszPath))
			SetFullSpec(tszPath);

		pMalloc->Free(pItemList);
	}
}

//	Operations
void CFileSpec::AppendToPath(LPCTSTR szExtra)
{
	ASSERT(szExtra);
	ASSERT(AfxIsValidString(szExtra));

	TCHAR tszTemp[_MAX_PATH];

	_tcsncpy(tszTemp, m_csPath, sizeof(tszTemp));
	_tcsncat(tszTemp, szExtra, countof(tszTemp) - (_tcslen(tszTemp) + _tcslen(szExtra)));
	PathAddBackslash(tszTemp);
	m_csPath = tszTemp;
}

void CFileSpec::CreateDirectory() const
{
	if (Exists())
		return;

	_tmkdir(GetFolder());
}

const CString CFileSpec::LastFolderInPath() const
{
	CString csTemp;
	TCHAR	tszWorking[_MAX_PATH],
			*tszPtr;

	_tcsncpy(tszWorking, m_csPath, countof(tszWorking));
	tszPtr = &tszWorking[_tcslen(tszWorking) - 1];

	csTemp = *tszPtr--;

	while (*tszPtr && *tszPtr != '\\')
		csTemp += *tszPtr--;

	csTemp.MakeReverse();
	return csTemp;
}

BOOL CFileSpec::LoadArchive(CObject *pObj) const
{
	CFile file;
	BOOL  bStatus = FALSE;

	ASSERT(pObj);
	ASSERT_VALID(pObj);
	ASSERT_KINDOF(CObject, pObj);
	ASSERT(pObj->IsSerializable());

	if (Exists())
	{
		try
		{
			if (file.Open(GetFullSpec(), CFile::modeRead | CFile::typeBinary | CFile::shareExclusive))
			{
				CArchive ar(&file, CArchive::load);

				pObj->Serialize(ar);
				ar.Close();
				file.Close();
				bStatus = TRUE;
			}
		}
		catch(CException *e)
		{
			e->Delete();
		}
	}

	return bStatus;
}

BOOL CFileSpec::SaveArchive(CObject *pObj) const
{
	CFile file;
	BOOL  bStatus = FALSE;

	ASSERT(pObj);
	ASSERT_VALID(pObj);
	ASSERT_KINDOF(CObject, pObj);
	ASSERT(pObj->IsSerializable());

	WriteAble();

	try
	{
		if (file.Open(GetFullSpec(), CFile::modeCreate | CFile::modeWrite | CFile::typeBinary | CFile::shareExclusive))
		{
			CArchive ar(&file, CArchive::store);

			pObj->Serialize(ar);
			ar.Close();
			file.Close();
			bStatus = TRUE;
		}
	}
	catch(CException *e)
	{
		e->Delete();
	}

	ReadOnly();
	return bStatus;
}

BOOL CFileSpec::IsUNCPath() const
{
	return IsUNCPath(m_csDrive);
}

BOOL CFileSpec::IsUNCPath(LPCTSTR szPath) const
{
	ASSERT(szPath);
	ASSERT(AfxIsValidString(szPath));

	if (_tcslen(szPath) < 2)
		return FALSE;
	
	return (szPath[0] == '\\' && szPath[1] == '\\');
}

BOOL CFileSpec::Exists() const
{
#ifdef _UNICODE
	return _waccess(GetFullSpec(), 0) == 0;
#else
	return _access(GetFullSpec(), 0) == 0;
#endif
}

void CFileSpec::WriteAble() const
{
#ifdef _UNICODE
	if (Exists())
		_wchmod(GetFullSpec(), _S_IWRITE | _S_IREAD);
#else
	if (Exists())
		_chmod(GetFullSpec(), _S_IWRITE | _S_IREAD);
#endif
}

void CFileSpec::ReadOnly() const
{
#ifdef _UNICODE
	if (Exists())
		_wchmod(GetFullSpec(), _S_IREAD);
#else
	if (Exists())
		_chmod(GetFullSpec(), _S_IREAD);
#endif
}

//	Access functions
const CString CFileSpec::GetFileName() const
{
	return m_csFilename + m_csExtension;
}

void CFileSpec::SetFileName(LPCTSTR szSpec)
{
	ASSERT(szSpec);
	ASSERT(AfxIsValidString(szSpec));

	TCHAR tszFilename[_MAX_FNAME],
		  tszExtension[_MAX_EXT];

	_tsplitpath(szSpec, NULL, NULL, tszFilename, tszExtension);
	m_csFilename = tszFilename;
	m_csExtension = tszExtension;
}

const CString	CFileSpec::FullPathNoExtension() const
{
	return m_csDrive + m_csPath + m_csFilename;
}

const CString CFileSpec::GetFolder() const
{
	return m_csDrive + m_csPath;
}

const CString CFileSpec::GetParentFolder() const
{
	TCHAR tszTemp[_MAX_PATH],
		  *tszPtr;
	
	_tcsncpy(tszTemp, GetFolder(), countof(tszTemp));
	tszPtr = &tszTemp[_tcslen(tszTemp) - 1];

	while (tszPtr > tszTemp && --tszPtr && *tszPtr != '\\')
		;

	*++tszPtr = '\0';
	return tszTemp;
}

const CString CFileSpec::GetFullSpec() const
{
	return m_csDrive + m_csPath + m_csFilename + m_csExtension;
}

void CFileSpec::SetFullSpec(FS_BUILTINS eSpec)
{
	Initialise(eSpec);
}

void CFileSpec::SetFullSpec(LPCTSTR szSpec)
{
	ASSERT(szSpec);
	ASSERT(AfxIsValidString(szSpec));

	TCHAR	tszDrive[_MAX_PATH],
			tszPath[_MAX_PATH],
			tszFilename[_MAX_FNAME],
			tszExtension[_MAX_EXT];
	LPCTSTR ptsz = szSpec;
	int		i = 4;

	if (IsUNCPath(szSpec))
	{
		//	UNC Paths are of the form \\server\share\path\filename[.ext]
		//	For the purpose if this code we treat the server/share portion
		//	as being logically equivalent to a drive and the rest of the 
		//	path then works the same way as for the older style path.
		m_csDrive = _T("");

		//	Brute force copy of the server/share part of the string
		while (i && *ptsz)
		{
			if (*ptsz == '\\')
				i--;

			if (i)
			{
				m_csDrive += *ptsz;

				ptsz++;
			}
		}

		_tsplitpath(ptsz, tszDrive, tszPath, tszFilename, tszExtension);
		m_csPath = tszPath;
		m_csFilename = tszFilename;
		m_csExtension = tszExtension;
	}
	else
	{

		_tsplitpath(ptsz, tszDrive, tszPath, tszFilename, tszExtension);
		m_csDrive = tszDrive;
		m_csPath = tszPath;
		m_csFilename = tszFilename;
		m_csExtension = tszExtension;
	}
}

const CString CFileSpec::ConvertToUNCPath() const
{
	USES_CONVERSION;

	CString csPath = GetFullSpec();

	if (IsUNCPath(csPath))
		return csPath;

	if (csPath[1] == ':')
	{
		// Fully qualified pathname including a drive letter, check if it's a mapped drive
		UINT uiDriveType = GetDriveType(m_csDrive);
		
		if (uiDriveType & DRIVE_REMOTE)
		{
			//	Yup - it's mapped so convert to a UNC path...
			TCHAR				tszTemp[_MAX_PATH];
			UNIVERSAL_NAME_INFO *uncName = (UNIVERSAL_NAME_INFO *) tszTemp;
			DWORD				dwSize = _MAX_PATH;
			DWORD				dwRet = WNetGetUniversalName(m_csDrive, REMOTE_NAME_INFO_LEVEL, uncName, &dwSize);
			CString				csDBShare;
			
			if (dwRet == NO_ERROR)
				return uncName->lpUniversalName + m_csPath + m_csFilename + m_csExtension;
		}
		else
		{
			//	It's a local drive so search for a share to it...
			NET_API_STATUS	res;
			PSHARE_INFO_502 BufPtr,
							p;
			DWORD			er = 0,
							tr = 0,
							resume = 0,
							i;
			int				iBestMatch = 0;
			CString			csTemp,
							csTempDrive,
							csBestMatch;
			
			do
			{
				res = NetShareEnum(NULL, 502, (LPBYTE *) &BufPtr, DWORD(-1), &er, &tr, &resume);

				//
				// If the call succeeds,
				//
				if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA)
				{
					csTempDrive = GetFolder();
					csTempDrive.MakeLower();
					p = BufPtr;

					//
					// Loop through the entries;
					//  print retrieved data.
					//
					for (i = 1; i <= er; i++)
					{
						if (p->shi502_type == STYPE_DISKTREE)
						{
							csTemp = W2T((LPWSTR) p->shi502_path);
							csTemp.MakeLower();

							if (csTempDrive.Find(csTemp) == 0)
							{
								//	We found a match
								if (iBestMatch < csTemp.GetLength())
								{
									iBestMatch = csTemp.GetLength();
									csBestMatch = W2T((LPWSTR) p->shi502_netname);
								}
							}
						}

						p++;
					}

					//
					// Free the allocated buffer.
					//
					NetApiBufferFree(BufPtr);

					if (iBestMatch)
					{
						TCHAR tszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
						DWORD dwBufLen = countof(tszComputerName);

						csTemp = GetFolder();
						csTemp = csTemp.Right(csTemp.GetLength() - iBestMatch + 1);
						GetComputerName(tszComputerName, &dwBufLen);
						csPath.Format(_T("\\\\%s\\%s%s%s%s"), tszComputerName, csBestMatch, csTemp, m_csFilename, m_csExtension);
					}
				}
				else 
					TRACE(_T("Error: %ld\n"), res);

				// Continue to call NetShareEnum while 
				//  there are more entries. 
				// 
			} while (res == ERROR_MORE_DATA); // end do
		}
	}

	return csPath;
}
